#include "DataProc.h"
#include "App/Utils/MapConv/MapConv.h"
#include "App/Utils/TrackFilter/TrackFilter.h"
#include "App/Utils/PointContainer/PointContainer.h"
#include "App/Config/Config.h"

#include <vector>

using namespace DataProc;

typedef struct
{
   MapConv mapConv;
   TrackPointFilter pointFilter;
   PointContainer* pointContainer;
   bool isStarted;
   bool isActive;
} TrackFilter_t;

static TrackFilter_t trackFilter;

static void onNotify(Account* account, TrackFilter_Info_t* info)
{
   switch (info->cmd)
   {
   case TRACK_FILTER_CMD_START:
       trackFilter.pointContainer = new PointContainer;
       trackFilter.pointFilter.Reset();
       trackFilter.isActive = true;
       trackFilter.isStarted = true;
       LV_LOG_USER("Track filter start");
       break;
   case TRACK_FILTER_CMD_PAUSE:
       trackFilter.isActive = false;
       LV_LOG_USER("Track filter pause");
       break;
   case TRACK_FILTER_CMD_CONTINUE:
       trackFilter.isActive = true;
       LV_LOG_USER("Track filter continue");
       break;
   case TRACK_FILTER_CMD_STOP:
   {
       trackFilter.isStarted = false;
       trackFilter.isActive = false;

       if (trackFilter.pointContainer)
       {
           delete trackFilter.pointContainer;
           trackFilter.pointContainer = nullptr;
       }

       uint32_t sum = 0, output = 0;
       trackFilter.pointFilter.GetCounts(&sum, &output);
       LV_LOG_USER(
           "Track filter stop, filted(%d%%): sum = %d, output = %d",
           sum ? (100 - output * 100 / sum) : 0,
           sum,
           output
       );
       break;
   }
   default:
       break;
   }
}

static void onPublish(Account* account, HAL::GPS_Info_t* gps)
{
   int32_t mapX, mapY;
   trackFilter.mapConv.ConvertMapCoordinate(
       gps->longitude,
       gps->latitude,
       &mapX,
       &mapY
   );

   if (trackFilter.pointFilter.PushPoint(mapX, mapY))
   {
       trackFilter.pointContainer->PushPoint(mapX, mapY);
   }
}

static int onEvent(Account* account, Account::EventParam_t* param)
{
   if (param->event == Account::EVENT_PUB_PUBLISH
           && param->size == sizeof(HAL::GPS_Info_t))
   {
       if (trackFilter.isActive)
       {
           onPublish(account, (HAL::GPS_Info_t*)param->data_p);
       }

       return Account::RES_OK;
   }

   if (param->size != sizeof(TrackFilter_Info_t))
   {
       return Account::RES_SIZE_MISMATCH;
   }

   switch (param->event)
   {
   case Account::EVENT_SUB_PULL:
   {
       TrackFilter_Info_t* info = (TrackFilter_Info_t*)param->data_p;
       info->pointCont = trackFilter.pointContainer;
       info->level = (uint8_t)trackFilter.mapConv.GetLevel();
       info->isActive = trackFilter.isStarted;
       break;
   }
   case Account::EVENT_NOTIFY:
       onNotify(account, (TrackFilter_Info_t*)param->data_p);
       break;

   default:
       break;
   }

    return Account::RES_OK;
}

DATA_PROC_INIT_DEF(TrackFilter)
{
   account->Subscribe("GPS");
   account->SetEventCallback(onEvent);

   trackFilter.pointContainer = nullptr;
   trackFilter.isActive = false;
   trackFilter.isStarted = false;

   trackFilter.mapConv.SetLevel(CONFIG_LIVE_MAP_LEVEL_DEFAULT);

   trackFilter.pointFilter.SetOffsetThreshold(CONFIG_TRACK_FILTER_OFFSET_THRESHOLD);
}
